/**
 * ImageUtil.java   2006-11-15
 *
 */
package com._21cn.framework.utils.image;

import com._21cn.framework.utils.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.awt.image.*;
import java.awt.geom.AffineTransform;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;


import java.awt.*;

/**
 * <p>Title: </p>
 * <p>Description:这是一个专门处理图片的类,缩略,旋转,水印 </p>
 *
 * @version 1.0
 */

public class ImageUtil{

	private static final Logger log = LoggerFactory.getLogger(ImageUtil.class);

	/**
	 * 为了图片压缩时较小失真，采用多步压缩的方法，当极大的图片压缩到极小时，效率较低，并且图像较模糊
	 */
	public static int ZOOM_MULTI_STEP = 1; 
	
	/**
	 * jdk内部的一种压缩算法，但效率略低于ZOOM_MULTI_STEP
	 */
	public static int ZOOM_IMAGE_GETSCALEDINSTANCE = 2; 
	
	/**
	 * LANCZOS压缩算法
	 */
	public static int ZOOM_LANCZOS = 3;
	
	/**
	 * TRILINEAR压缩算法
	 */
	public static int ZOOM_TRILINEAR = 4;
	
	/**
	 * 图片文件相关信息
	 *
	 */
	public static class ImageInfo{
		private int width = 0;
		private int height = 0;
		private int colorSpace = 0;
		private BufferedImage image = null;
		private FileUtil.FileInfo fileInfo;
		
		/**
		 * 构造函数
		 * @param fileInfo 文件信息
		 */
		public ImageInfo(FileUtil.FileInfo fileInfo){
			this.fileInfo = fileInfo;
		}
		
		/**
		 * 构造函数
		 * @param fileName 图片文件名，全路径
		 */
		public  ImageInfo( String fileName ){
			this( new  File(fileName) );
		}
		
		/**
		 * 构造函数
		 * @param file 图片文件
		 */
		public  ImageInfo( File file ){
			this( new FileUtil.FileInfo( file ) );
		}

		/**
		 * @return 返回 颜色空间类型。
		 */
		public int getColorSpace() {
			getImage();
			return colorSpace;
		}
		/**
		 * @return 返回 文件信息。
		 */
		public FileUtil.FileInfo getFileInfo() {
			return fileInfo;
		}
		
		/**
		 * @return 返回 图片高度，单位像素。
		 */
		public int getHeight() {
			getImage();
			return height;
		}

		/**
		 * @return 返回 图片宽度，单位像素。
		 */
		public int getWidth() {
			getImage();
			return width;
		}
		/**
		 * @return 返回 图片对象。
		 */
		public BufferedImage getImage() {
			if( image != null )
				return image;
			try{
				this.image = ImageUtil.getBufferedImage( fileInfo.getObj() );
				
				this.width = this.image.getWidth();
				this.height = this.image.getHeight();
				this.colorSpace = this.image.getColorModel().getColorSpace().getType();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return image;
		}
	}
	
	/**
	 * 获取文件名对应的图片对象
	 * @param imageFileName 图片文件名，全路径
	 * @return Image图片对象
	 * @throws IOException
	 */
	public static Image getImage( String imageFileName )  throws IOException{
		return getImage( new File( imageFileName) );
	}

	/** 
	 * 获取文件名对应的图片对象
	 * ImageIO.read( File f ) 这个方法有bug:
	 *   某些jpeg读入后图像会变暗，改用ImageIcon来读取图片
	 * 
	 * Toolkit.getDefaultToolkit().getImage( String filename )这个方法也有bug:
	 *   某些jpeg读入后，再保存回jpeg会出现null指针错误，改用ImageIcon来读取图片
	 * 
	 * new ImageIcon( String filename ).getImage()这个方法也有bug:
	 *   当一个jpeg文件不是真正的jpeg格式时会出错，改回ImageIo.read来读取
	 */
	public static Image getImage( File imageFile )  throws IOException{
		
		String fileType = FileUtil.getFileType( imageFile.getName() );
		
		if( fileType.equals("jpeg") || fileType.equals("jpg") ){
			ImageIcon icon = new ImageIcon( imageFile.getAbsolutePath() );
			if( icon.getImageLoadStatus() ==  MediaTracker.COMPLETE )
				return icon.getImage();
		}
		return  ImageIO.read( imageFile );
		
	}
	
	/**
	 * 读取图片文件
	 * @param imageFile 图片文件
	 * @return 图片对象
	 * @throws IOException
	 */
	public static BufferedImage getBufferedImage( File imageFile )  throws IOException{
		return getBufferedImage( getImage( imageFile ) );
	}
	
	/**
	 * 读取图片文件
	 * @param imageFileName 图片文件名，全路径
	 * @return BufferedImage 图片对象
	 * @throws IOException
	 */
	public static BufferedImage getBufferedImage( String imageFileName )  throws IOException{
		return getBufferedImage( new File( imageFileName) );
	}

	/**
	 * 转化image 为BufferedImage类型
	 * @param image 源图片对象
	 * @return BufferedImage 图片对象
	 */
	public static BufferedImage getBufferedImage( Image image ) {
		if( image instanceof BufferedImage )
			return (BufferedImage)image;
		
		int w = image.getWidth(null);
		int h = image.getHeight(null);
		
		BufferedImage srcImage = new BufferedImage( w , h , BufferedImage.TYPE_INT_RGB );
        Graphics2D g = srcImage.createGraphics();
		g.drawImage(image, 0, 0, null); 
        g.dispose();   
		return srcImage;
	}

	
	
	/**
	 * 缩小图片,产生缩略图
	 * @param srcPath 源图片路径
	 * @param destPath 放小后放置路径
	 * @param newWidth 缩小后的宽度
	 * @param newHeight 缩小后的高度
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long zoom(String srcPath, String destPath, int newWidth, int newHeight ) {
		try {
			return zoom( getImage(srcPath), destPath,  newWidth,  newHeight);
		} catch (Exception e) {
			e.printStackTrace();
			return 0L;
		}
	}
	
	/**
	 * 缩小图片,产生缩略图
	 * @param srcImage 源图片对象
	 * @param destPath 放小后放置路径
	 * @param newWidth 缩小后的宽度
	 * @param newHeight 缩小后的高度
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long zoom( Image srcImage, String destPath, int newWidth, int newHeight)  throws IOException{ //Buffered
		BufferedImage img = zoomImage( srcImage, newWidth, newHeight );
		return writeImage( img, destPath );
	}

	/**
	 * 缩小图片,产生缩略图
	 * @param srcImage 源图片对象
	 * @param destPath 放小后放置路径
	 * @param newWidth 缩小后的宽度
	 * @param newHeight 缩小后的高度
	 * @param method 缩放的算法选择子
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long zoom( Image srcImage, String destPath, int newWidth, int newHeight, int method)  throws IOException{ //Buffered
		BufferedImage img = zoomImage( srcImage, newWidth, newHeight, method );
		return writeImage( img, destPath );
	}

	/**
	 * 缩小图片,产生缩略图
	 * @param srcImage 源图片对象
	 * @param newWidth 缩小后的宽度
	 * @param newHeight 缩小后的高度
	 * @return BufferedImage
	 */
	public static BufferedImage zoomImage( Image srcImage, int newWidth, int newHeight){//Buffered
		return zoomImage( srcImage, newWidth, newHeight, ZOOM_MULTI_STEP );
	}
	
	/**
	 * 缩小图片,产生缩略图
	 * @param srcImage 源图片对象
	 * @param newWidth 缩小后的宽度
	 * @param newHeight 缩小后的高度
	 * @param method 缩放的算法选择子
	 * @return BufferedImage
	 */
	public static BufferedImage zoomImage( Image srcImage, int newWidth, int newHeight, int method){//Buffered
		try {
			if( srcImage== null || newWidth < 0 || newHeight < 0 )
				return null;
			int w = srcImage.getWidth(null);
			int h = srcImage.getHeight(null);
			if( w<= newWidth && h<=newHeight ) {
				return getBufferedImage(srcImage);
			}
			else if( newWidth == 0 && newHeight ==0 ){
				return getBufferedImage(srcImage);
			}
			else if( newWidth == 0 ){
				newWidth = w * newHeight/h;
			}
			else if( newHeight == 0 ){
				newHeight = h * newWidth/w;
			}
			
			if( w<= newWidth && h<=newHeight ) {
				return getBufferedImage(srcImage);
			}
			// 对于300万以上的大图片来讲ZOOM_MULTI_TIMES的效率最高
			if(method == ZOOM_MULTI_STEP )
				return ScaleImage.scaleImageByMultiStep(getBufferedImage(srcImage),newWidth,newHeight, true );
			if( method == ZOOM_IMAGE_GETSCALEDINSTANCE )
				return  getBufferedImage(getBufferedImage(srcImage).getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH));
			if( method == ZOOM_TRILINEAR )
				return ScaleImage.scaleImageByTrilinear( getBufferedImage(srcImage), newWidth, newHeight );
			return ScaleImage.scaleImageByLanczos( getBufferedImage(srcImage), newWidth, newHeight );

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		
	}

	/**
	 * 缩小图片,产生缩略图
	 * @param srcImage 源图片对象
	 * @param maxWidth 缩小后的最大宽度
	 * @param maxHeight 缩小后的最大高度
	 * @return BufferedImage
	 */
	public static BufferedImage zoomMax( Image srcImage, int maxWidth, int maxHeight ) { //Buffered
		int w = srcImage.getWidth(null);
		int h = srcImage.getHeight(null);
		int[] newWidHgt = getRealWidthHeight( w, h, maxWidth, maxHeight );
		
		if( newWidHgt[0] == w && newWidHgt[1] == h )
			return getBufferedImage( srcImage );
		
		return zoomImage( srcImage,  newWidHgt[0],  newWidHgt[1]);
	}
	
	/**
	 * 缩小图片,产生缩略图
	 * @param srcPath 源图片文件名，全路径
	 * @param maxWidth 缩小后的最大宽度
	 * @param maxHeight 缩小后的最大高度
	 * @return BufferedImage
	 */
	public static BufferedImage zoomMax(String srcPath, int maxWidth, int maxHeight ) {
		try {
			return zoomMax( getImage(srcPath), maxWidth,  maxHeight);
		}
		catch (Exception e) {
			e.printStackTrace();
			log.error( e + ":" + srcPath );
			return null;
		}
	}

	/**
	 * 缩小图片,产生缩略图
	 * @param srcPath 源图片路径
	 * @param destPath 放小后放置路径
	 * @param maxWidth 放小后的最大宽度
	 * @param maxHeight 放小后的最大高度
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long zoomMax(String srcPath, String destPath, int maxWidth, int maxHeight ) {
		try {
			return zoomMax( getImage(srcPath), destPath,  maxWidth,  maxHeight);
		} catch (Exception e) {
			e.printStackTrace();
			log.error( srcPath + ":" + e );
			return 0L;
		}
	}


	/**
	 * 缩小图片,产生缩略图
	 * @param srcImage 源图片对象
	 * @param destPath 放小后放置路径
	 * @param maxWidth 放小后的最大宽度
	 * @param maxHeight 放小后的最大高度
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long zoomMax( Image srcImage, String destPath, int maxWidth, int maxHeight )  throws IOException{ 
		return zoomMax( srcImage,  destPath, maxWidth, maxHeight, false );
	}
	
	/**
	 * 缩小图片,产生缩略图
	 * @param srcImage 源图片对象
	 * @param destPath 放小后放置路径
	 * @param maxWidth 放小后的最大宽度
	 * @param maxHeight 放小后的最大高度
	 * @param force 是否强制转换
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long zoomMax( Image srcImage, String destPath, int maxWidth, int maxHeight, boolean force )  throws IOException { 
		if( srcImage== null || destPath == null || maxWidth < 0 || maxHeight < 0 )
			return 0L;
		
		int w = srcImage.getWidth(null);
		int h = srcImage.getHeight(null);
		int[] newWidHgt = getRealWidthHeight( w, h, maxWidth, maxHeight );
		if( !force && newWidHgt[0] >= w && newWidHgt[1] >= h ) return 0L;
		return zoom( srcImage, destPath,  newWidHgt[0],  newWidHgt[1] );
	}
	
	/**
	 * 求出最大限定范围内的实际宽高
	 * @param w 原来的宽度
	 * @param h 原来的高度
	 * @param maxWidth 最大宽度，如果是0表示不限制，以高为准等比例处理
	 * @param maxHeight 最大高度，如果是0表示不限制，以宽为准等比例处理
	 * @return 最大限定范围内的实际宽高
	 */
	public static int[] getRealWidthHeight( int w, int h, int maxWidth, int maxHeight ){
		int[] wh = new int[2];
		log.debug( " w:" + w+ ",h:" + h+ ",maxWidth:" + maxWidth+ ",maxHeight:" + maxHeight );
		
		float r0 = (float)w/(float)h;
		if( maxWidth == 0 && maxHeight ==0 ){
			wh[0] = w;
			wh[1] = h;
			return wh;
		}
		else if( maxWidth == 0 ){
			maxWidth = (int)(r0 * maxHeight);
		}
		else if( maxHeight == 0 ){
			maxHeight = (int)( maxWidth/r0);
		}
		
		wh[0] = maxWidth;
		wh[1] = maxHeight;
		float rm = (float)maxWidth/(float)maxHeight;
		if( rm > r0 ){
			if( h< wh[1] ){
				wh[0] = w;
				wh[1] = h;
			}
			wh[0] = (int)( r0 * wh[1]);
		}
		else{
			if( w< wh[0] ){
				wh[0] = w;
				wh[1] = h;
			}
			wh[1] = (int)( wh[0]/r0);
		}
		wh[0] = wh[0]<=0 ?  w:wh[0];
		wh[1] = wh[1]<=0 ?  h:wh[1];
		return wh;
	}
	
	/**
	 * 求出最小限定范围内的实际宽高
	 * @param w 原来的宽度
	 * @param h 原来的高度
	 * @param minWidth  最小的宽度，如果是0表示不限制，以高为准等比例处理
	 * @param minHeight 最小的高度，如果是0表示不限制，以宽为准等比例处理
	 * @return 最小限定范围内的实际宽高
	 */
	public static int[] getRealWidthHeightForMin( int w, int h, int minWidth, int minHeight ){
		int[] wh = new int[2];
		
		float r0 = (float)w/(float)h;
		if( minWidth == 0 && minHeight ==0 ){
			wh[0] = w;
			wh[1] = h;
			//log.debug( "mw,mh:0,0" );
			return wh;
		}
		else if( minWidth == 0 ){
			minWidth = (int)(minHeight * r0);
			//log.debug( "mw:0," + minWidth );
		}
		else if( minHeight == 0 ){
			minHeight = (int)( minWidth/ r0);
			//log.debug( "mh:0," + minHeight  );
		}
		minWidth = minWidth>w? w : minWidth;
		minHeight = minHeight>h? h : minHeight;
		
		wh[0] = minWidth;
		wh[1] = minHeight;
		float rm = (float)minWidth/(float)minHeight;
		//log.debug( "rm,r0:"+ rm + "," +r0 );
		if( rm > r0 ){
			if( w< wh[1] ){
				//log.debug( "w<wh[1]:"+ w + "," +wh[1] );
				wh[0] = w;
				//wh[1] = h;
			}
			wh[1] = (int)(wh[0]/r0);
			//log.debug( "1:wh[0,1]:"+ wh[0] + "," +wh[1] );
		}
		else{
			if( h< wh[1] ){
				//log.debug( "h<wh[1]:"+ h + "," +wh[1] );
				//wh[0] = w;
				wh[1] = h;
			}
			wh[0] = (int)(r0 * wh[1]);
			//log.debug( "2:wh[0,1]:"+ wh[0] + "," +wh[1] );
		}
		return wh;
	}

	/**
	 * 获取图片文件的相关信息
	 * @param fileName 图片文件名
	 * @return 图片文件的相关信息
	 */
	public static ImageInfo getImageInfo( String fileName ){
		return  new ImageInfo( fileName );
	}
	
	/**
	 * 获取图片文件的相关信息
	 * @param file 图片文件
	 * @return 图片文件的相关信息
	 */
	public static ImageInfo getImageInfo( File file ){
		return new ImageInfo(  file );
	}

	/**
	 * 获取图片文件的相关信息
	 * @param fileInfo 图片文件
	 * @return 图片文件的相关信息
	 */
	public static ImageInfo getImageInfo( FileUtil.FileInfo fileInfo ){
		return new ImageInfo( fileInfo );
	}


	/**
	 * 旋转图片
	 * @param srcPath 源图片路径
	 * @param destPath 目标路径
	 * @param degree 旋转角度
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long rotate( String srcPath,String destPath, int degree ){
		try {
			return rotate( getBufferedImage(srcPath), destPath,  degree );
		} catch (Exception e) {
			e.printStackTrace();
			return 0L;
		}
	}

	/**
	 * 旋转图片
	 * @param srcImage 源图片
	 * @param destPath 目标路径
	 * @param degree 旋转角度
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long rotate( BufferedImage srcImage ,String destPath, int degree)  throws IOException{
		BufferedImage img = rotateImg(srcImage, degree, Color.WHITE );
		return writeImage( img, destPath );
	}
	
	/**
	 * 旋转图片
	 * @param image 源图片
	 * @param degree 旋转角度
	 * @param bgcolor 空白地方的北京填充色
	 * @return BufferedImage
	 */
	public static BufferedImage rotateImg( BufferedImage image, int degree, Color bgcolor ){
		
		degree=degree%360;
		if( degree == 0 ) return image;
		
		if(degree<0)degree=360+degree;//将角度转换到0-360度之间
		double ang=degree * Math.PI / 180;//将角度转为弧度
		
		int iw = image.getWidth();//原始图象的宽度 
		int ih = image.getHeight();//原始图象的高度  

		/**
		 *确定旋转后的图象的高度和宽度
		 */
		int w=0;
		int h=0; 
		
		if( degree == 180 ){
			w = iw; 
			h = ih; 
		}else if( degree == 90 || degree == 270 ){ 
			w = ih; 
			h = iw;  
		}else{  
			double sin = Math.abs(Math.sin(ang) );
			double cos = Math.abs(Math.cos(ang) );
			w = (int)(iw * cos + ih * sin);
			h = (int)(ih * cos + iw * sin);
		}
		
		BufferedImage rotatedImage=new BufferedImage(w,h,image.getType()); 
		Graphics gs=rotatedImage.getGraphics();
		gs.setColor(bgcolor);
		gs.fillRect(0,0,w,h);//以给定颜色绘制旋转后图片的背景
		AffineTransform at=new AffineTransform();
		at.rotate(ang,w/2,h/2);//旋转图象

		int x = (w/2)-(iw/2);//确定原点坐标
		int y = (h/2)-(ih/2); 
		at.translate(x,y); 
		AffineTransformOp op=new AffineTransformOp(at,AffineTransformOp.TYPE_NEAREST_NEIGHBOR); 
		op.filter(image, rotatedImage); 
		image=rotatedImage;
		return image;
	}
	
	/**
	 * 切割图片
	 * @param srcImageFileName 源图片文件名
	 * @param x 起点x坐标，单位像素
	 * @param y 起点y坐标，单位像素
	 * @param w 目标宽度，单位像素
	 * @param h 目标高度，单位像素
	 * @param destPath 目标文件名，全路径
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long crop( String  srcImageFileName, int x, int y, int w, int h, String destPath ){
		try {
			return crop( getBufferedImage(srcImageFileName), x, y, w, h, destPath );
		} catch (Exception e) {
			e.printStackTrace();
			return 0L;
		}
	}

	/**
	 * 切割图片
	 * @param srcImage 源图片
	 * @param x 起点x坐标，单位像素
	 * @param y 起点y坐标，单位像素
	 * @param w 目标宽度，单位像素
	 * @param h 目标高度，单位像素
	 * @param destPath 目标文件名，全路径
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long crop( BufferedImage srcImage, int x, int y, int w, int h, String destPath ) throws IOException{
		BufferedImage img =  cropImage( srcImage, x, y, w, h );
		return writeImage( img, destPath );
	}

	
	/**
	 * 切割图片
	 * @param srcImage 源图片
	 * @param x 起点x坐标，单位像素
	 * @param y 起点y坐标，单位像素
	 * @param w 目标宽度，单位像素
	 * @param h 目标高度，单位像素
	 * @return BufferedImage
	 */
	public static BufferedImage cropImage( BufferedImage srcImage, int x, int y, int w, int h ){
		int sw = srcImage.getWidth();
		int sh = srcImage.getHeight();

        x = ( x<0 ? ( sw + x ) : ( x>=sw ? 0 : x ) );
        y = ( y<0 ? ( sh + y ) : ( y>=sh ? 0 : y ) );
        w = ( (x + w) >= sw ? ( sw>x ? (sw - x) : sw ) : w );
        h = ( (y + h) >= sh ? ( sh>y ? (sh - y) : sh ) : h );
        //w = w<=0 ? sw: w;
        //h = h<=0 ? sh: h;
        return srcImage.getSubimage( x, y, w, h );
	}

	/**
	 * 切割出符合要求的缩略图，并保存到指定文件中
	 * @param srcImageFileName 源图片文件名
	 * @param minW 目标宽度，单位像素
	 * @param minH 目标高度，单位像素
	 * @param destPath 目标文件名，全路径
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long cropMinImage( String srcImageFileName, int minW, int minH, String destPath ){
		try {
			return cropMinImage( getImage(srcImageFileName), minW, minH, destPath );
		} catch (Exception e) {
			e.printStackTrace();
			return 0L;
		}
	}

	/**
	 * 切割出符合要求的缩略图
	 * @param srcImageFileName 源图片文件名
	 * @param minW 目标宽度，单位像素
	 * @param minH 目标高度，单位像素
	 * @return BufferedImage
	 */
	public static BufferedImage cropMinImage( String srcImageFileName, int minW, int minH ){
		try {
			return cropMinImage( getImage(srcImageFileName), minW, minH );
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 切割出符合要求的缩略图，并保存到指定文件中
	 * @param srcImage 源图片
	 * @param minW 目标宽度，单位像素
	 * @param minH 目标高度，单位像素
	 * @param destPath 目标文件名，全路径
	 * @return 如果成功返回目标文件的大小,否则返回0
	 */
	public static long cropMinImage( Image srcImage, int minW, int minH, String destPath ) throws IOException{
		BufferedImage img = cropMinImage( srcImage, minW, minH );
		return writeImage( img, destPath );
	}
	
	/**
	 * 切割出符合要求的缩略图
	 * @param srcImage 源图片
	 * @param minW 目标宽度，单位像素
	 * @param minH 目标高度，单位像素
	 * @return BufferedImage
	 */
	public static BufferedImage cropMinImage( Image srcImage, int minW, int minH ){
		int sw = srcImage.getWidth(null);
		int sh = srcImage.getHeight(null);
		float r = ((float)minW)*sh/minH/sw;
		int minWzoom = minW;
		int minHzoom = minH;
		if( r < 1 ){
			minWzoom = (int)(minW*1.1);
			minHzoom = (int)(minH*1.1);
		}
		int[] newWH = getRealWidthHeightForMin( sw, sh, minWzoom, minHzoom );
		//log.debug( "3:newWH[0,1]:"+ newWH[0] + "," +newWH[1] );
		BufferedImage image = zoomImage( srcImage, newWH[0], newWH[1] );
		log.debug( "zoomImage min wh: "+ image.getWidth() + ","+ image.getHeight() );
		
        int x = ( newWH[0] - minW ) / 2;
        int y = (int)(( newWH[1] - minH ) / (4*r));
        int targetW = (x>=0? minW : newWH[0]);
        int targetH = (y>=0? minH : newWH[1]);
        x = (x>=0? x : 0 );
        y = (y>=0? y : 0 );
        log.debug("x:"+x+",y:"+y+",targetW:"+targetW+",targetH:"+targetH +",r:"+r  );
        return cropImage( image, x, y, targetW, targetH );
	}

	/**
	 * 加水印，并保存到指定文件中
	 * @param srcImageFileName 源图片文件名
	 * @param iconFileName 水印图标图片
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param alpha 叠加透明度0-1
	 * @param destPath 目标文件名，全路径
	 * @return 如果成功返回目标文件的大小,否则返回0
	 * @throws IOException
	 */
	public static long waterMark( String srcImageFileName, String iconFileName, int x, int y, float alpha, String destPath )  throws IOException{
		BufferedImage img = waterMark( srcImageFileName , iconFileName, x, y, alpha );
		return writeImage( img, destPath );
	}

	/**
	 * 加水印
	 * @param srcImageFileName 源图片文件名
	 * @param iconFileName 水印图标文件名
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param alpha 叠加透明度0-1
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage waterMark( String srcImageFileName, String iconFileName, int x, int y, float alpha ){
		try {
			return waterMark( new File( srcImageFileName ), getBufferedImage(iconFileName), x, y, alpha );
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 加水印
	 * @param srcImageFileName 源图片文件名
	 * @param icon 水印图标
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param alpha 叠加透明度0-1
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage waterMark( String srcImageFileName, BufferedImage icon, int x, int y, float alpha ){
		return waterMark( new File( srcImageFileName ), icon, x, y, alpha );
	}
	
	/**
	 * 加水印
	 * @param srcImageFile 源图片文件
	 * @param icon 水印图标
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param alpha 叠加透明度0-1
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage waterMark( File srcImageFile, BufferedImage icon, int x, int y, float alpha ){
		try {
			BufferedImage srcImage = getBufferedImage( srcImageFile );
			return waterMark( srcImage, icon, x, y, alpha );
		} catch (Exception e) {
			log.warn( "watermark error:" + e );
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 加水印
	 * @param srcImage 源图片
	 * @param icon 水印图标
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param alpha 叠加透明度0-1
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage waterMark( BufferedImage srcImage, BufferedImage icon, int x, int y, float alpha ){
		int sw = srcImage.getWidth();
		int sh = srcImage.getHeight();
		int iw = icon.getWidth();
		int ih = icon.getHeight();
        //BufferedImage bimage   =   new   BufferedImage( sw, sh, BufferedImage.TYPE_INT_RGB );
        BufferedImage bimage   =   srcImage;
        Graphics2D g = bimage.createGraphics();
        //g.drawImage( srcImage, 0, 0, sw, sh,  null); 
        if( alpha > 1f || alpha < 0f )
        	alpha = 1f;
        AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha );
        g.setComposite(ac);
        
        log.debug( "watermark before: x:"+x + ",y:" + y );
        x = x<0 ? (sw -iw + x): x;
        y = y<0 ? (sh -ih + y): y;
        x = ( x + iw >= sw? (sw - iw) : x );
        y = ( y + ih >= sh? (sh - ih) : y );
        x = x<0 ? 0: x;
        y = y<0 ? 0: y;
        log.debug( "watermark after: x:"+x + ",y:" + y );
        if( (x+iw>sw) || (y+ih>sh) ){
        	int dw = (x+iw)>sw ? (sw-x) :iw; 
        	int dh = (y+ih)>sh ? (sh-y) :ih; 
            BufferedImage image   = icon.getSubimage( 0, 0, dw, dh );
            g.drawImage(image,x,y,null);   
        }
        else
        	g.drawImage(icon,x,y,iw,ih,null);   
        g.dispose();   
		return bimage;
	}
	
	/**
	 * 文字叠加
	 * @param srcImageFileName 源图片文件名
	 * @param txt 要叠加的文字
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param txtColor 文字颜色
	 * @param bgColor 文字背景颜色
	 * @param fontName 字体名称
	 * @param fontSize 字体大小
	 * @param alpha 透明度
	 * @param destPath 目标文件名，全路径
	 * @return 如果成功返回目标文件的大小,否则返回0
	 * @throws IOException
	 */
	public static long drawText( String srcImageFileName, String txt, int x, int y, String txtColor, String bgColor, String fontName, int fontSize, float alpha, String destPath )  throws IOException{
		BufferedImage img = drawText( srcImageFileName , txt, x, y, txtColor, bgColor, fontName,fontSize, alpha );
		return writeImage( img, destPath );
	}

	/**
	 * 文字叠加
	 * @param srcImageFileName 源图片文件名
	 * @param txt 要叠加的文字
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param txtColor 文字颜色
	 * @param bgColor 文字背景颜色
	 * @param fontName 字体名称
	 * @param fontSize 字体大小
	 * @param alpha 透明度
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage drawText( String srcImageFileName, String txt, int x, int y, String txtColor, String bgColor, String fontName, int fontSize, float alpha ){
		File fin = new File(srcImageFileName);  
		return drawText( fin, txt, x, y, txtColor, bgColor, fontName, fontSize, alpha );
	}

	/**
	 * 文字叠加
	 * @param srcImageFile 源图片文件
	 * @param txt 要叠加的文字
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param txtColor 文字颜色
	 * @param bgColor 文字背景颜色
	 * @param fontName 字体名称
	 * @param fontSize 字体大小
	 * @param alpha 透明度
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage drawText( File srcImageFile, String txt, int x, int y, String txtColor, String bgColor, String fontName, int fontSize, float alpha ){
		try {
			BufferedImage srcImage = getBufferedImage( srcImageFile );
			return drawText( srcImage, txt, x, y, txtColor, bgColor, fontName, fontSize, alpha );
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 文字叠加
	 * @param srcImage 源图片
	 * @param txt 要叠加的文字
	 * @param x 叠加的x坐标，单位像素
	 * @param y 叠加的y坐标，单位像素
	 * @param txtColor 文字颜色
	 * @param bgColor 文字背景颜色
	 * @param fontName 字体名称
	 * @param fontSize 字体大小
	 * @param alpha 透明度
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage drawText( BufferedImage srcImage, String txt, int x, int y, String txtColor, String bgColor, String fontName, int fontSize, float alpha ){
		Color _txtColor = new Color(Integer.parseInt( txtColor, 16));
		Color _bgColor = new Color(Integer.parseInt( bgColor, 16));
		Font font = new Font( fontName, 1, fontSize);
		return drawText( srcImage, txt, x, y, _txtColor, _bgColor, font, alpha);
	}
	
	/**
	 * 文字叠加
	 * @param srcImage 源图片
	 * @param txt 要叠加的文字
	 * @param x 叠加的x坐标，单位像素，负数表示坐标原点为右下角
	 * @param y 叠加的y坐标，单位像素，负数表示坐标原点为右下角
	 * @param txtColor 文字颜色
	 * @param bgColor 文字背景颜色
	 * @param font 字体
	 * @param alpha 透明度
	 * @return BufferedImage
	 * @throws IOException
	 */
	public static BufferedImage drawText( BufferedImage srcImage, String txt, int x, int y, Color txtColor, Color bgColor, Font font, float alpha ){
		int sw = srcImage.getWidth();
		int sh = srcImage.getHeight();
        //BufferedImage bimage   =   new   BufferedImage( sw, sh, BufferedImage.TYPE_INT_RGB );
        BufferedImage bimage   =   srcImage;
        Graphics2D g = bimage.createGraphics();
        //g.drawImage( srcImage, 0, 0, sw, sh,  null);
        if( alpha > 1f || alpha < 0f )
        	alpha = 1f;
        AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha );
        g.setComposite(ac);
        g.setFont( font );
        g.setColor( txtColor ); 
        g.setBackground( bgColor );   
        int iw = g.getFontMetrics().stringWidth( txt );

        x = x<0 ? (sw -iw + x): x;
        y = y<0 ? sh + y: y;
        x = ( x + iw >= sw? sw - iw : x );
        x = x<0 ? 0: x;
        y = y<0 ? 0: y;
        g.drawString( txt , x, y);   //添加文字   
        g.dispose();   
		return bimage;
	}
	
	/**
	 * 将图片保存到文件中
	 * @param img 图片
	 * @param destPath 目标文件
	 * @return 返回文件的大小
	 * @throws IOException
	 */
	public static long writeImage( BufferedImage img, String destPath ) throws IOException{
		File fout = new File(destPath); // 将要转换出的文件
		String fileType = FileUtil.getFileType( fout.getName() ); 
		ImageIO.write( img, fileType, fout );
		return fout.length();
	}
   
	/**
	 * 将图片保存到指定输出流中
	 * @param img 图片
	 * @param fileType 目标图片编码类型，支持jpg,png,bmp等格式，不支持gif格式
	 * @param out 目标输出流
	 * @return true
	 * @throws IOException
	 */
	public static boolean writeImage( BufferedImage img, String fileType, OutputStream out ) throws IOException{
		ImageIO.write( img, fileType, out );
		return true;
	}

	/**
	 * 正弦曲线Wave扭曲图片
	 * @param srcImg 图片路径
	 * @param bXDir 如果扭曲则选择为True
	 * @param dMultValue 波形的幅度倍数，越大扭曲的程度越高，一般为3
	 * @param dPhase 波形的起始相位，取值区间[0-2*PI)
	 * @return
	 */
	public static BufferedImage twistImage( BufferedImage srcImg, boolean bXDir, double dMultValue, double dPhase ) {
		int w = srcImg.getWidth();
		int h = srcImg.getHeight();
		BufferedImage destImg = new BufferedImage( w, h, BufferedImage.TYPE_INT_RGB );

		// 将位图背景填充为白色
		Graphics g = destImg.getGraphics();
		g.setColor( Color.WHITE );
		g.fillRect( 0, 0, w, h );
		g.dispose();

		double dBaseAxisLen = bXDir ? (double)h : (double)w;

		for( int i = 0; i < w; i++ ) {
			for( int j = 0; j < h; j++ ) {
				double dx = 0;
				dx = bXDir ? (Math.PI * 2 * (double)j) / dBaseAxisLen : (Math.PI * 2 * (double)i) / dBaseAxisLen;
				dx += dPhase;
				double dy = Math.sin( dx );

				// 取得当前点的颜色
				int nOldX = 0, nOldY = 0;
				nOldX = bXDir ? i + (int)(dy * dMultValue) : i;
				nOldY = bXDir ? j : j + (int)(dy * dMultValue);

				int color = srcImg.getRGB( i, j );
				if( nOldX >= 0 && nOldX < w && nOldY >= 0 && nOldY < h ) {
					destImg.setRGB( nOldX, nOldY, color );
				}
			}
		}

		return destImg;
	}
	
	public static void main( String[] args ) throws Exception{
		//String[] names = ImageIO.getReaderFormatNames();
		//for( int i=0; i<names.length; i++)
		//	System.out.println( names[i] );
		
		//int[] wh = getRealWidthHeight( 1024,768,2560,2560 );
		//System.out.println( wh[0] );
		//System.out.println( wh[1] );
		
		//rotate( "d:/temp/00111.jpg", "d:/temp/test/rotate0.jpg", 0 );
		//rotate( "d:/temp/00111.jpg", "d:/temp/test/rotate30.jpg", 30 );
		//rotate( "d:/temp/00111.jpg", "d:/temp/test/rotate45.jpg", 45 );
		//rotate( "d:/temp/00111.jpg", "d:/temp/test/rotate60.jpg", 60 );
		//rotate( "d:/temp/00111.jpg", "d:/temp/test/rotate90.jpg", 90 );
		//rotate( "d:/temp/00111.jpg", "d:/temp/test/rotate180.jpg", 180 );
		//rotate( "d:/temp/00111.jpg", "d:/temp/test/rotate270.jpg", 270 );
		//rotate( "d:/temp/8744316.jpg", "d:/temp/test/rotate180.jpg", 180 );
		
		//cropMinImage( "d:/temp/8781705.jpg",  120, 100, "d:/temp/test/cropmin1.jpg" );
		//crop( "d:/temp/00111.jpg",  -200, 0, 200, 100, "d:/temp/crop1.jpg" );
		//waterMark( "d:/temp/00111.jpg", "d:/temp/biaoshi.gif" , -100, -40, 0.2f ,"d:/temp/test/watermark1.jpg" );
		//waterMark( "d:/temp/00111.jpg", "d:/temp/tellogo.gif" , -100, -40, 0.8f ,"d:/temp/test/watermark2.jpg" );
		
		//BufferedImage img = drawText( "d:/temp/crop1.jpg", "中文发财" , -100, 30 , "ff0000","ffffff", "宋体", 20, 1.0f );
		//img = ImageUtil.twistImage( img, false, 5, 3.0f );
		//ImageUtil.writeImage( img, "d:/temp/drawText2.png" );
		//drawText( "d:/temp/00111.jpg", "中文test by Jimmy" , -100, 40 , "ff0000","ffffff", "宋体", 24, 1.0f, "d:/temp/drawText1.png");
		//zoomMax( "d:/temp/8744316.jpg", "d:/temp/test/zoom1301.jpg", 300,500 );
		//System.out.println (zoomMax( "d:/temp/00111.jpg", "d:/temp/test/zoom1302.jpg", 200,0 ));
		//zoom( "d:/temp/00111-bmp.bmp", "d:/temp/test/zoom130-130bmp.jpg", 130,130 );
		//zoom( "d:/temp/00111-png.png", "d:/temp/test/zoom130-130png.png", 130,130 );
		//zoom( "d:/temp/8781705.jpg", "d:/temp/test/zoom130-130.jpg", 130,130 );
		//zoom( "d:/temp/00111-jpg.jpg", "d:/temp/test/zoom130-130jpg.jpg", 130,130 );
		//zoom( "d:/temp/test/0011.jpg", "d:/temp/test/0011.jpg", 500,0 );
		zoomMax( "d:/temp/2.jpg", "d:/temp/jvm_scale_200.jpg", 200,300 );
		//zoomMax( "d:/temp/0011.jpg", "d:/temp/test/zoommax1024.jpg", 1024,1024 );
		
		//long s = System.currentTimeMillis();
		//for( int i=1; i< 100; i++)
		//	drawText( "d:/temp/00111.jpg", "中文test by Jimmy" , -100, -40 , "ff0000","ffffff", "宋体", 14, 0.5f, "d:/temp/test/drawText1.png");
		//System.out.println( System.currentTimeMillis() - s );

		//ImageInfo info  = getImageInfo( "d:/temp/0011.jpg" );
		//getImageInfo( "d:/temp/8781705.jpg" );
		//System.out.print( (HashMap)info.getBaseExif() );
	}
	

}

